package body sigma is

   -----------
   -- creer --
   -----------

   function creer return modele is
   begin
      return new terme'(selon => creer);
   end creer;

   --------------
   -- inscrire --
   --------------

   function inscrire (C0:modele; I1:individu) return modele is
   begin
      return new terme'(selon => inscrire,
                        inscrire_c0 => c0,
                        inscrire_I1 => new individu'(I1));
   end inscrire;

   -------------
   -- bloquer --
   -------------

   function bloquer (C0:modele; I1,I2: individu) return modele is
   begin
      return new terme'(selon => bloquer,
                        bloquer_c0 => C0,
                        bloquer_I1 => new individu'(I1),
                        bloquer_I2 => new individu'(I2));
   end bloquer;

   ---------------
   -- debloquer --
   ---------------

   function debloquer (C0:modele; I1,I2: individu) return modele is
   begin
      return new terme'(selon => debloquer,
                        debloquer_c0 =>c0,
                        debloquer_I1 => new individu'(I1),
                        debloquer_I2 => new individu'(I2));
   end debloquer;

   --------------------
   -- ajouter_cercle --
   --------------------

   function ajouter_cercle
     (C0:modele;
      I1: individu;
      C1:cercle)
      return modele
   is
   begin
      return new terme'(selon => ajouter_cercle,
                        ajouter_cercle_C0 => c0,
                        ajouter_cercle_I1 => new individu'(I1),
                        ajouter_cercle_C1 => new cercle'(C1));
   end ajouter_cercle;

   --------------------
   -- retirer_cercle --
   --------------------

   function retirer_cercle
     (C0:modele;
      I1: individu;
      C1:cercle)
      return modele
   is
   begin
      return new terme'(selon => retirer_cercle,
                        retirer_cercle_C0 => c0,
                        retirer_cercle_I1 => new individu'(I1),
                        retirer_cercle_C1 => new cercle'(C1));
   end retirer_cercle;

   --------------------
   -- ajouter_membre --
   --------------------

   function ajouter_membre
     (C0:modele;
      I1: individu;
      C1:cercle;
      I2: individu)
      return modele
   is
   begin
      return new terme'(selon => ajouter_membre,
                        ajouter_membre_C0 => c0,
                        ajouter_membre_I1 => new individu'(I1),
                        ajouter_membre_C1 => new cercle'(C1),
                        ajouter_membre_I2 => new individu'(I2));
   end ajouter_membre;

   --------------------
   -- retirer_membre --
   --------------------

   function retirer_membre
     (C0:modele;
      I1: individu;
      C1:cercle;
      I2: individu)
      return modele
   is
   begin
      return new terme'(selon => retirer_membre,
                        retirer_membre_C0 => c0,
                        retirer_membre_I1 => new individu'(I1),
                        retirer_membre_C1 => new cercle'(C1),
                        retirer_membre_I2 => new individu'(I2));
   end retirer_membre;

   ------------
   -- poster --
   ------------

   function poster
     (C0:modele;
      I1:individu;
      C1:cercle;
      T: message)
      return modele
   is
   begin
      return new terme'(selon => poster,
                        poster_C0 => c0,
                        poster_I1 => new individu'(I1),
                        poster_C1 => new cercle'(C1),
                        poster_T => new message'(T));
   end poster;

   ---------------
   -- commenter --
   ---------------

   function commenter
     (C0: modele;
      I1:individu;
      I:Natural;
      T: message)
      return modele
   is
   begin
      return new terme'(selon => commenter,
                        commenter_C0 => c0,
                        commenter_I1 => new individu'(I1),
                        commenter_I => I,
                        commenter_T => new message'(T));
   end commenter;

   -----------------
   -- est_inscrit --
   -----------------

   function est_inscrit (C0:modele; I1:individu) return boolean is
   begin
      case C0.selon is
         when creer => return false;
         when inscrire =>
            if(I1=c0.inscrire_I1.all)then return true;
            else return est_inscrit(c0.inscrire_C0,I1);
            end if;
         when bloquer =>
            return est_inscrit(c0.bloquer_C0,I1);
         when debloquer =>
            return est_inscrit(c0.debloquer_C0,I1);
         when ajouter_cercle =>
            return est_inscrit(c0.ajouter_cercle_C0,I1);
         when retirer_cercle =>
            return est_inscrit(c0.retirer_cercle_C0,I1);
         when ajouter_membre =>
            return est_inscrit(c0.ajouter_membre_C0,I1);
         when retirer_membre =>
            return est_inscrit(c0.retirer_membre_C0,I1);
         when poster =>
            return  est_inscrit(c0.poster_C0,I1);
         when commenter =>
            return est_inscrit(c0.commenter_C0,I1);
      end case;
   end est_inscrit;

   --------------------
   -- nombre_inscrit --
   --------------------

   function nombre_inscrit (C0:modele) return natural is
   begin
      case C0.selon is
         when creer => return 0;
         when inscrire => return nombre_inscrit(c0.inscrire_C0)+1;
         when bloquer => return nombre_inscrit(c0.bloquer_C0);
         when debloquer => return nombre_inscrit(c0.debloquer_C0);
         when ajouter_cercle => return nombre_inscrit(c0.ajouter_cercle_C0);
         when retirer_cercle => return nombre_inscrit(c0.retirer_cercle_C0);
         when ajouter_membre => return nombre_inscrit(c0.ajouter_membre_C0);
         when retirer_membre => return nombre_inscrit(c0.retirer_membre_C0);
         when poster => return nombre_inscrit(c0.poster_C0);
         when commenter => return nombre_inscrit(c0.commenter_C0);
      end case;
   end nombre_inscrit;

   ------------------
   -- ieme_inscrit --
   ------------------

   function ieme_inscrit (C0:modele; I:Natural) return individu is
   begin
      case C0.selon is
         when creer => raise Program_Error;
         when inscrire =>
            if I <= nombre_inscrit(c0.inscrire_C0) then
               if ieme_inscrit(c0.inscrire_C0, I) < c0.inscrire_I1.all then
                  return ieme_inscrit(c0.inscrire_C0, I);
               else
                  if I=1 then
                     return c0.inscrire_I1.all;
                  else
                     if ieme_inscrit(C0.inscrire_C0, I-1) <= C0.inscrire_I1.all then
                        return c0.inscrire_I1.all;
                     else
                        return ieme_inscrit(C0.inscrire_C0, i-1);
                     end if;
                  end if;
               end if;

            else
               if i=1 then
                  return c0.inscrire_I1.all;
               else
                  if ieme_inscrit(C0.inscrire_C0, I-1) <= c0.inscrire_I1.all then
                     return c0.inscrire_I1.all;
                  else
                     return ieme_inscrit(C0.inscrire_C0, I-1) ;
                  end if;
               end if;

            end if;

         when bloquer => return ieme_inscrit(c0.bloquer_C0, I);
         when debloquer => return ieme_inscrit(c0.debloquer_C0, I);
         when ajouter_cercle => return ieme_inscrit(c0.ajouter_cercle_C0, I);
         when retirer_cercle => return ieme_inscrit(c0.retirer_cercle_C0, I);
         when ajouter_membre => return ieme_inscrit(c0.ajouter_membre_C0, I);
         when retirer_membre => return ieme_inscrit(c0.retirer_membre_C0, I);
         when poster => return ieme_inscrit(c0.poster_C0, I);
         when commenter => return ieme_inscrit(c0.commenter_C0, I);
      end case;
   end ieme_inscrit;

   ----------------
   -- est_bloque --
   ----------------

   function est_bloque (C0:modele; I1,I2:individu) return boolean is
   begin
      case C0.selon is
         when creer => return false;
         when inscrire => return est_bloque(c0.inscrire_C0, I1, I2);
         when bloquer =>
            if I1=c0.bloquer_I1.all AND I2=c0.bloquer_I2.all then
               return true;
            else
               return est_bloque(c0.bloquer_C0, I1, I2);
            end if;
         when debloquer =>
            if I1=c0.debloquer_I1.all AND I2=c0.debloquer_I2.all then
               return false;
            else
               return est_bloque(c0.debloquer_C0, I1, I2);
            end if;
         when ajouter_cercle => return est_bloque(c0.ajouter_cercle_C0, I1, I2);
         when retirer_cercle => return est_bloque(c0.retirer_cercle_C0, I1, I2);
         when ajouter_membre => return est_bloque(c0.ajouter_membre_C0, I1, I2);
         when retirer_membre => return est_bloque(c0.retirer_membre_C0, I1, I2);
         when poster => return est_bloque(c0.poster_C0, I1, I2);
         when commenter => return est_bloque(c0.commenter_C0, I1, I2);
      end case;
   end est_bloque;

   -------------------
   -- est_cercle_de --
   -------------------

   function est_cercle_de (C0:modele; I1:individu; C1:cercle) return boolean
   is
   begin
      case C0.selon is
         when creer => return false;
         when inscrire =>
            if c0.inscrire_I1.all=I1 then
               return false;
            else
               return est_cercle_de(c0.inscrire_C0, I1, C1);
            end if;
         when bloquer => return est_cercle_de(c0.bloquer_C0, I1, C1);
         when debloquer => return est_cercle_de(c0.debloquer_C0, I1, C1);
         when ajouter_cercle =>
            if c0.ajouter_cercle_I1.all=I1 AND c0.ajouter_cercle_C1.all=C1 then
               return true;
            else
               return est_cercle_de(c0.ajouter_cercle_C0, I1, C1);
            end if;
         when retirer_cercle =>
            if c0.retirer_cercle_I1.all=I1 AND c0.retirer_cercle_C1.all=C1 then
               return false;
            else
               return est_cercle_de(c0.retirer_cercle_C0, I1, C1);
            end if;
         when ajouter_membre => return est_cercle_de(c0.ajouter_membre_C0, I1, C1);
         when retirer_membre => return est_cercle_de(c0.retirer_membre_C0, I1, C1);
         when poster => return est_cercle_de(c0.poster_C0, I1, C1);
         when commenter => return est_cercle_de(c0.commenter_C0, I1, C1);
      end case;
   end est_cercle_de;

   -----------------
   -- nombre_cercle_de --
   -----------------

   function nombre_cercle_de(C0:modele;I1:individu) return natural is
   begin
      case C0.selon is
         when creer => return 0;
         when inscrire =>
            if c0.inscrire_I1.all=I1 then
               return 0;
            else
               return nombre_cercle_de(c0.inscrire_C0, I1);
            end if ;
         when bloquer => return nombre_cercle_de(c0.bloquer_C0, I1);
         when debloquer => return nombre_cercle_de(c0.debloquer_C0, I1);
         when ajouter_cercle =>
            if c0.ajouter_cercle_I1.all=I1 then
               return nombre_cercle_de(c0.ajouter_cercle_C0, I1) +1;
            else
               return nombre_cercle_de(c0.ajouter_cercle_C0, I1);
            end if ;
         when retirer_cercle =>
            if c0.retirer_cercle_I1.all=I1 then
               return nombre_cercle_de(c0.retirer_cercle_C0, I1) -1;
            else
               return nombre_cercle_de(c0.retirer_cercle_C0, I1);
            end if ;
         when ajouter_membre => return nombre_cercle_de(c0.ajouter_membre_C0, I1);
         when retirer_membre => return nombre_cercle_de(c0.retirer_membre_C0, I1);
         when poster => return nombre_cercle_de(c0.poster_C0, I1);
         when commenter => return nombre_cercle_de(c0.commenter_C0, I1);
      end case;
   end nombre_cercle_de;
   -----------------
   -- ieme_cercle --
   -----------------

   function ieme_cercle (C0:modele; I1:individu; I:natural) return cercle is
   begin
      case C0.selon is
         when creer => raise Program_Error;
         when ajouter_cercle =>
            if I1 = C0.ajouter_cercle_I1.all then
            if I <= nombre_cercle_de(c0.ajouter_cercle_C0,I1) then
               if ieme_cercle(c0.ajouter_cercle_C0,I1, I) < c0.ajouter_cercle_C1.all then
                  return ieme_cercle(c0.ajouter_cercle_C0,I1, I);
               else
                  if I=1 then
                     return c0.ajouter_cercle_C1.all;
                  else
                     if ieme_cercle(C0.ajouter_cercle_C0,I1, I-1) <= C0.ajouter_cercle_C1.all then
                        return c0.ajouter_cercle_C1.all;
                     else
                        return ieme_cercle(C0.ajouter_cercle_C0,I1, i-1);
                     end if;
                  end if;
               end if;

            else
               if i=1 then
                  return c0.ajouter_cercle_C1.all;
               else
                  if ieme_cercle(C0.ajouter_cercle_C0,I1, I-1) <= c0.ajouter_cercle_C1.all then
                     return c0.ajouter_cercle_C1.all;
                  else
                     return ieme_cercle(C0.ajouter_cercle_C0,I1, I-1) ;
                  end if;
               end if;

               end if;
            else
               return ieme_cercle(c0.ajouter_cercle_C0,I1, I);
            end if;

         when bloquer => return ieme_cercle(c0.bloquer_C0,I1, I);
         when debloquer => return ieme_cercle(c0.debloquer_C0,I1, I);
         when inscrire => return ieme_cercle(c0.inscrire_C0,I1, I);
         when retirer_cercle =>
            if c0.retirer_cercle_I1.all=I1 then
               if c0.retirer_cercle_C1.all <= ieme_cercle(c0.retirer_cercle_C0, I1,I) then
                  return ieme_cercle(C0.retirer_cercle_C0,I1,I+1);
               else
                  return ieme_cercle(C0.retirer_cercle_C0,I1,I);
               end if;
            else
               return ieme_cercle(C0.retirer_cercle_C0,I1,I);
            end if;


         when ajouter_membre => return ieme_cercle(c0.ajouter_membre_C0,I1, I);
         when retirer_membre => return ieme_cercle(c0.retirer_membre_C0,I1, I);
         when poster => return ieme_cercle(c0.poster_C0,I1, I);
         when commenter => return ieme_cercle(c0.commenter_C0,I1, I);
      end case;
   end ieme_cercle;

   -------------------
   -- est_membre_de --
   -------------------

   function est_membre_de
     (C0:modele;
      I1:individu;
      C1:cercle;
      I2:individu)
      return boolean
   is
   begin
      case C0.selon is
         when creer => return false;
         when inscrire => return est_membre_de(C0.inscrire_C0,I1,C1,I2);
         when bloquer =>
            if I1=C0.bloquer_I1.all AND I2=C0.bloquer_I2.all then
               return false;
            else
               return est_membre_de(c0.bloquer_C0,I1,C1,I2);
            end if;
         when debloquer => return est_membre_de(c0.debloquer_C0,I1,C1,I2);
         when ajouter_cercle =>
            if I1=C0.ajouter_cercle_I1.all AND C1=C0.ajouter_cercle_C1.all then
               return false;
            else
               return est_membre_de(c0.ajouter_cercle_C0,I1,C1,I2);
            end if;
         when retirer_cercle =>
            if I1=C0.retirer_cercle_I1.all AND C1=C0.retirer_cercle_C1.all then
               return false;
            else
               return est_membre_de(c0.retirer_cercle_C0,I1,C1,I2);
            end if;
         when ajouter_membre =>
            if I1=C0.ajouter_membre_I1.all AND C1=C0.ajouter_membre_C1.all AND i2=c0.ajouter_membre_I2.all then
               return true;
            else
               return est_membre_de(c0.ajouter_membre_C0,I1,C1,I2);
            end if;
         when retirer_membre =>
            if I1=C0.retirer_membre_I1.all AND C1=C0.retirer_membre_C1.all then
               return false;
            else
               return est_membre_de(c0.retirer_membre_C0,I1,C1,I2);
            end if;
         when poster => return est_membre_de(c0.poster_C0,I1,C1,I2);
         when commenter => return est_membre_de(c0.commenter_C0,I1,C1,I2);
      end case;
   end est_membre_de;

   -------------------
   -- nombre_membre --
   -------------------

   function nombre_membre_de (C0:modele; I1:individu; C1:cercle) return natural
   is
   begin
      case C0.selon is
         when creer => return 0;
         when inscrire => return nombre_membre_de(c0.inscrire_C0, I1, C1);
         when bloquer =>
            if I1=c0.bloquer_I1.all AND est_membre_de(C0.bloquer_C0, I1, C1, C0.bloquer_I2.all) then
               return nombre_membre_de(c0.bloquer_C0, I1, C1)-1;
            else
               return nombre_membre_de(c0.bloquer_C0, I1, C1);
            end if;
         when debloquer => return nombre_membre_de(c0.debloquer_C0, I1, C1);
         when ajouter_cercle =>
            if c0.ajouter_cercle_I1.all=I1 AND c1=c0.ajouter_cercle_C1.all then
               return 0;
            else
               return nombre_membre_de(c0.ajouter_cercle_C0, I1, C1);
            end if ;
         when retirer_cercle =>
            if c0.retirer_cercle_I1.all=I1 AND c1=c0.retirer_cercle_C1.all then
               return 0;
            else
               return nombre_membre_de(c0.retirer_cercle_C0, I1, C1);
            end if ;
         when ajouter_membre =>
            if c0.ajouter_membre_I1.all=I1 AND c1=c0.ajouter_membre_C1.all then
               return nombre_membre_de(c0.ajouter_membre_C0, I1, C1)+1;
            else
               return nombre_membre_de(c0.ajouter_membre_C0, I1, C1);
            end if ;
         when retirer_membre =>
            if c0.retirer_membre_I1.all=I1 AND c1=c0.retirer_membre_C1.all then
               return nombre_membre_de(c0.retirer_membre_C0, I1, C1)-1;
            else
               return nombre_membre_de(c0.retirer_membre_C0, I1, C1);
            end if ;
         when poster => return nombre_membre_de(c0.poster_C0, I1, C1);
         when commenter => return nombre_membre_de(c0.commenter_C0, I1, C1);
      end case;
   end nombre_membre_de;

   -----------------
   -- ieme_membre --
   -----------------

   function ieme_membre (C0:modele; I1:individu;C1:cercle ; I: natural) return individu is
   begin
      case C0.selon is
         when creer => raise Program_Error;
         when inscrire => return ieme_membre(c0.inscrire_C0, I1, C1, I) ;
         when bloquer =>
            if I1=c0.bloquer_I1.all AND est_membre_de(c0.bloquer_C0, I1, C1, c0.bloquer_I2.all) then
               if c0.bloquer_I2.all <= ieme_membre(c0.bloquer_C0, I1, C1, I) then
                  return ieme_membre(c0.bloquer_C0, I1, C1, I+1);
               else
                  return ieme_membre(c0.bloquer_C0, I1, C1, I);
               end if;
            else
               return ieme_membre(c0.bloquer_C0, I1, C1, I);
            end if;
         when debloquer => return ieme_membre(c0.debloquer_C0, I1, C1, I) ;
         when ajouter_cercle => return ieme_membre(c0.ajouter_cercle_C0, I1, C1, I) ;
         when retirer_cercle => return ieme_membre(c0.retirer_cercle_C0, I1, C1, I) ;
         when ajouter_membre =>
            if I1=c0.ajouter_membre_I1.all AND C1=c0.ajouter_membre_C1.all then
               if I <= nombre_membre_de(c0.ajouter_membre_C0, I1, C1) then
                  if ieme_membre(c0.ajouter_membre_C0, I1, C1, I) <= c0.ajouter_membre_I2.all then
                     return ieme_membre(C0.ajouter_membre_C0, I1, C1, I);
                  else
                     if i = 1 then
                        return c0.ajouter_membre_I2.all;
                     else
                        if ieme_membre(c0.ajouter_membre_C0, I1, C1, i-1) <= c0.ajouter_membre_I2.all then
                           return c0.ajouter_membre_I2.all;
                        else
                           return ieme_membre(c0.ajouter_membre_C0, I1, C1, I-1);
                        end if;
                     end if ;
                  end if;
               else
                  if i=1 then
                     return c0.ajouter_membre_I2.all;
                  else
                     if ieme_membre(c0.ajouter_membre_C0, I1, C1, I-1) <= c0.ajouter_membre_I2.all then
                        return c0.ajouter_membre_I2.all;
                     else
                        return ieme_membre(c0.ajouter_membre_C0, I1, C1, I);
                     end if;
                  end if;
               end if;
            else return ieme_membre(c0.ajouter_membre_C0, I1, C1, I);
            end if;
         when retirer_membre =>
            if I1=c0.retirer_membre_I1.all AND est_membre_de(c0.retirer_membre_C0, I1, C1, c0.retirer_membre_I2.all) then
               if c0.retirer_membre_I2.all <= ieme_membre(c0.retirer_membre_C0, I1, C1, I) then
                  return ieme_membre(c0.retirer_membre_C0, I1, C1, I+1);
               else
                  return ieme_membre(c0.retirer_membre_C0, I1, C1, I);
               end if;
            else
               return ieme_membre(c0.retirer_membre_C0, I1, C1, I);
            end if;
         when poster => return ieme_membre(c0.poster_C0, I1, C1, I) ;
         when commenter => return ieme_membre(c0.commenter_C0, I1, C1, I) ;
      end case;
   end ieme_membre;

   ---------------
   -- consulter --
   ---------------

   function consulter (C0:modele; I1:individu) return Mur is
   begin
      case C0.selon is
         when creer => raise Program_Error;
         when inscrire =>
            if(I1=c0.inscrire_I1.all)then return creer(I1);
            else return consulter(c0.inscrire_C0,I1);
            end if;
         when bloquer =>
            return consulter(c0.bloquer_C0,I1);
         when debloquer =>
            return consulter(c0.debloquer_C0,I1);
         when ajouter_cercle =>
            return consulter(c0.ajouter_cercle_C0,I1);
         when retirer_cercle =>
            return consulter(c0.retirer_cercle_C0,I1);
         when ajouter_membre =>
            return consulter(c0.ajouter_membre_C0,I1);
         when retirer_membre =>
            return consulter(c0.retirer_membre_C0,I1);
         when poster =>
            if (est_membre_de(C0.poster_C0,C0.poster_I1.all,C0.poster_C1.all,I1) AND
                  not est_bloque(C0.poster_C0,I1,C0.poster_I1.all))
              OR C0.poster_I1.all=I1 then
               return ajouter(consulter(C0.poster_C0,I1),c0.poster_T.all,C0.poster_I1.all, nombre_post(C0.poster_C0)+1);
            else
               return consulter(C0.poster_C0,I1);
            end if;
         when commenter =>
            if est_defini (consulter(C0.commenter_C0,I1),C0.commenter_I) AND
              ((C0.commenter_I1.all=I1)OR not(est_bloque(C0.commenter_C0, auteur(consulter(C0.commenter_C0,I1),C0.commenter_I),C0.commenter_I1.all)
                                              OR est_bloque(C0.Commenter_C0,C0.commenter_I1.all,I1)
                                              OR est_bloque(C0.Commenter_C0,I1,C0.commenter_I1.all))) then
               return ajouter(consulter(C0.Commenter_C0,I1),C0.Commenter_T.all,C0.commenter_I1.all,C0.Commenter_I);
            else
               return consulter(C0.commenter_C0,I1);
            end if;
      end case;
   end consulter;

   ---------------
   -- nombre_post --
   ---------------

   function nombre_post (C0:modele) return natural is
   begin
      case C0.selon is
         when creer => return 0;
         when inscrire => return nombre_post(c0.inscrire_c0);
         when bloquer => return nombre_post(c0.bloquer_c0);
         when debloquer => return nombre_post(c0.debloquer_c0);
         when ajouter_cercle => return nombre_post(c0.ajouter_cercle_c0);
         when retirer_cercle => return nombre_post(c0.retirer_cercle_c0);
         when ajouter_membre => return nombre_post(c0.ajouter_membre_c0);
         when retirer_membre => return nombre_post(c0.retirer_membre_c0);
         when poster => return nombre_post(c0.poster_c0)+ 1;
         when commenter => return nombre_post(c0.commenter_c0);
      end case;
   end nombre_post;

end sigma;
